package com.a1anwang.okble.beacon;


import com.a1anwang.okble.client.scan.BLEScanResult;
import com.a1anwang.okble.client.scan.DeviceScanCallBack;
import com.a1anwang.okble.client.scan.OKBLEScanManager;
import com.a1anwang.okble.common.OKBLEDataUtils;
import ohos.app.Context;
import ohos.bluetooth.BluetoothHost;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by a1anwang.com on 2018/5/30.
 */
public class OKBLEBeaconManager {
    private static final int EVENT_MESSAGE = 1001;
    private Context mContext;
    private OKBLEScanManager okbleScanManager;
    private int monitoringBeaconRegionId = 0;
    private OKBLEBeaconScanCallback scanCallback;
    private OKBLEBeaconRegionListener regionListener;
    private Map<String, RegionObject> monitoringBeaconRegions = new HashMap<String, RegionObject>();

    // 退出区域的超时时间，持续regionExitOverTime这么长的时间内没有再次扫描到这个区域，则视为退出区域
    private static final int regionExitOverTime = 10 * 1000;

    // create()的参数是true时，则为托管模式
    private EventRunner runner = EventRunner.create(true);
    private MyEventHandler myHandler = new MyEventHandler(runner);

    public void setBeaconScanCallback(OKBLEBeaconScanCallback mCallback) {
        this.scanCallback = mCallback;
    }

    public void setRegionListener(OKBLEBeaconRegionListener regionListener) {
        this.regionListener = regionListener;
    }

    /**
     * 构造函数
     *
     * @param context 上下文
     */
    public OKBLEBeaconManager(Context context) {
        super();
        this.mContext = context;
        init();
    }

    private void init() {
        okbleScanManager = new OKBLEScanManager(mContext);
        okbleScanManager.setScanCallBack(new DeviceScanCallBack() {
            @Override
            synchronized public void onBLEDeviceScan(BLEScanResult device, int rssi) {
                byte[] beaconData = formatIbeaconData(device);
                if (beaconData != null) {

                    // 扫描到iBeacon设备
                    OKBLEBeacon okbleBeacon = new OKBLEBeacon();
                    okbleBeacon.setDevice(BluetoothHost.getDefaultHost(mContext).getRemoteDev(device.getMacAddress()));
                    okbleBeacon.setUuid(formatUuidFromIbeaconData(beaconData));
                    okbleBeacon.setMajor(formatMajorFromIbeaconData(beaconData));
                    okbleBeacon.setMinor(formatMinorFromIbeaconData(beaconData));
                    okbleBeacon.setRssi(rssi);
                    okbleBeacon.setMeasuredPower(formatMajorFromIbeaconData(beaconData));
                    okbleBeacon.setName(BluetoothHost.getDefaultHost(mContext).getRemoteDev(device.getMacAddress()).getDeviceName().toString());
                    if (scanCallback != null) {
                        scanCallback.onScanBeacon(okbleBeacon);
                    }
                    if (monitoringBeaconRegions.size() > 0) {
                        String key = okbleBeacon.getIdentifier();
                        if (monitoringBeaconRegions.containsKey(key)) {
                            // 如果正在监控这个区域
                            RegionObject regionObject = monitoringBeaconRegions.get(key);
                            handleEnterRegion(regionObject);
                        }
                        // 这里的-1是对应了OKBLEBeaconRegion里的major，minor的默认值为-1
                        key = okbleBeacon.getUuid() + "_-1_-1";
                        if (monitoringBeaconRegions.containsKey(key)) {

                            // 如果正在监控这个区域
                            RegionObject regionObject = monitoringBeaconRegions.get(key);
                            handleEnterRegion(regionObject);
                        }
                        key = okbleBeacon.getUuid() + "_" + okbleBeacon.getMajor() + "_-1";
                        if (monitoringBeaconRegions.containsKey(key)) {

                            // 如果正在监控这个区域
                            RegionObject regionObject = monitoringBeaconRegions.get(key);
                            handleEnterRegion(regionObject);
                        }
                    }
                }
            }

            @Override
            public void onFailed(int code) {
            }

            @Override
            public void onStartSuccess() {

            }
        });
    }

    /**
     * 开始扫描iBeacon
     */
    public void startScanBeacon() {
        okbleScanManager.stopScan();
        okbleScanManager.startScan();
    }

    public boolean isScanning() {
        return okbleScanManager.isScanning();
    }

    /**
     * 停止扫描iBeacon
     */
    public void stopScan() {
        okbleScanManager.stopScan();
        myHandler.removeAllEvent();
    }

    /**
     * startMonitoringForRegion 开始
     *
     * @param region OKBLEBeaconRegion对象
     */
    public void startMonitoringForRegion(OKBLEBeaconRegion region) {
        String key = region.getIdentifier();
        if (!monitoringBeaconRegions.containsKey(key)) {
            monitoringBeaconRegionId++;
            RegionObject regionObject = new RegionObject(region, monitoringBeaconRegionId);
            monitoringBeaconRegions.put(key, regionObject);
            if (!isScanning()) {
                startScanBeacon();
            }
        }
    }

    /**
     * stopMonitoringForRegion 停止
     *
     * @param region OKBLEBeaconRegion对象
     */
    public void stopMonitoringForRegion(OKBLEBeaconRegion region) {
        String key = region.getIdentifier();
        monitoringBeaconRegions.remove(key);
    }

    private byte[] formatIbeaconData(BLEScanResult scanResult) {
        if (scanResult.getAdvertisingData() != null && scanResult.getAdvertisingData().length == 30) {
            return null;
        }
        Map<Integer, byte[]> manufacturerData = scanResult.getManufacturerSpecificData();
        if (manufacturerData == null || manufacturerData.size() == 0) {
            return null;
        }
        for (Map.Entry<Integer, byte[]> entry : manufacturerData.entrySet()) {
            byte[] value = entry.getValue();
            if (entry.getKey() == 0x004C) {
                if (value != null && value.length == 23 && value[0] == 0x02 && value[1] == 0x15) {
                    return value;
                }
            }
        }
        return null;
    }

    private String formatUuidFromIbeaconData(byte[] beaconData) {
        byte[] uuidValue = new byte[16];
        System.arraycopy(beaconData, 2, uuidValue, 0, 16);
        String uuid = "";
        String hexStr = OKBLEDataUtils.BytesToHexString(uuidValue);
        uuid = hexStr.substring(0, 8);
        uuid += "-";
        uuid += hexStr.substring(8, 12);
        uuid += "-";
        uuid += hexStr.substring(12, 16);
        uuid += "-";
        uuid += hexStr.substring(16, 20);
        uuid += "-";
        uuid += hexStr.substring(20, 32);
        return uuid;
    }

    private int formatMajorFromIbeaconData(byte[] beaconData) {
        int major = OKBLEDataUtils.buildUint16(beaconData[18], beaconData[19]);
        return major;
    }

    private int formatMinorFromIbeaconData(byte[] beaconData) {
        int minor = OKBLEDataUtils.buildUint16(beaconData[20], beaconData[21]);
        return minor;
    }

    private int formatMeasuredPowerFromIbeaconData(byte[] beaconData) {
        int measuredPower = beaconData[22];
        return measuredPower;
    }

    private void handleEnterRegion(RegionObject regionObject) {
        myHandler.removeEvent(regionObject.regionID);
        Object object = regionObject.region.getIdentifier();
        InnerEvent delayInnerEvent = InnerEvent.get(EVENT_MESSAGE, 0L, object);
        myHandler.sendEvent(delayInnerEvent, regionExitOverTime, EventHandler.Priority.IMMEDIATE);

        if (!regionObject.hasEntered) {
            regionObject.hasEntered = true;
            if (regionListener != null) {
                if (okbleScanManager.isScanning()) {
                    regionListener.onEnterBeaconRegion(regionObject.region);
                }
            }
        }
    }

    class MyEventHandler extends EventHandler {
        private MyEventHandler(EventRunner runner) {
            super(runner);
        }

        // 重写实现processEvent方法
        @Override
        public void processEvent(InnerEvent event) {
            super.processEvent(event);
            if (event == null) {
                return;
            }
            int eventId = event.eventId;
            switch (eventId) {
                case EVENT_MESSAGE:
                    // 待执行的操作，由开发者定义
                    String key = (String) event.object;

                    // 收到消息，表示已经持续了一段时间没有扫描到区域内的beacon了，视为退出区域
                    if (monitoringBeaconRegions.containsKey(key)) {
                        RegionObject regionObject = monitoringBeaconRegions.get(key);
                        OKBLEBeaconRegion beaconRegion = regionObject.region;

                        regionObject.hasEntered = false;
                        if (regionListener != null) {
                            if (okbleScanManager.isScanning()) {
                                regionListener.onExitBeaconRegion(beaconRegion);
                            }
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    }

    public void requestLocationPermission() {
        if (okbleScanManager != null) {
            okbleScanManager.requestLocationPermission();
        }
    }

    /**
     * OKBLEBeaconScanCallback ble扫描回调接口
     */
    public interface OKBLEBeaconScanCallback {
        /**
         * onScanBeacon  开始扫描回调
         *
         * @param beacon OKBLEBeacon对象
         */
        void onScanBeacon(OKBLEBeacon beacon);
    }

    /**
     * OKBLEBeaconRegionListener Region监听
     */
    public interface OKBLEBeaconRegionListener {
        /**
         * onEnterBeaconRegion 输入
         *
         * @param beaconRegion OKBLEBeaconRegion对象
         */
        void onEnterBeaconRegion(OKBLEBeaconRegion beaconRegion);

        /**
         * onExitBeaconRegion 退出
         *
         * @param beaconRegion OKBLEBeaconRegion对象
         */
        void onExitBeaconRegion(OKBLEBeaconRegion beaconRegion);

        /**
         * onRangeBeaconsInRegion onRangeBeaconsInRegion
         *
         * @param beacons List<OKBLEBeacon> OKBLEBeacon列表
         */
        void onRangeBeaconsInRegion(List<OKBLEBeacon> beacons);
    }

    protected static final class RegionObject {
        boolean hasEntered;
        OKBLEBeaconRegion region;
        int regionID;

        public RegionObject(OKBLEBeaconRegion region, int regionID) {
            super();
            this.region = region;
            this.regionID = regionID;
        }
    }

}